#pragma once
#include"../LibDGGSKernel/IDGGSKernel.hpp"

_NNU_DGGS_BEGIN
using namespace Base;
namespace Field
{
    template <typename T>
    class DGGSFieldBand;

    /// <summary>
    /// 格网数据的field的Partition结构定义
    /// </summary>
    template<typename T>
    class DGGSFieldPartition
    {
        friend class DGGSFieldBand<T>;
    private:
        DGGSGridType    _gridType = DGGSGridType::Unknown;      //格网类型
        int             _level = 1;                             //层级
        int             _partitionIndex = -1;                   //基菱号
        DGGridBBox      _gridBox;                               //包围盒
        T*              _data = nullptr;                        //属性数据
        double          _noDataValue =noData;                   //无效值
        IDGGSKernel*    _pKernel = nullptr;                     //用于编码转化

        DGGSFieldPartition(int level, DGGSGridType gridType);

        /// <summary>
        /// 设置无效值
        /// </summary>
        /// <param name="nodataValue">无效值</param>
        bool setNoDataValue(T noDataValue) ;

    public:

        DGGSFieldPartition() = default;

        ~DGGSFieldPartition();

        /// <summary>
        /// 设置基菱号
        /// </summary>
        /// <param name="partitionIndex">基菱号</param>
        void setPartitionIndex(int partitionIndex);

        /// <summary>
        /// 获取基菱号
        /// </summary>
        int getPartitionIndex() { return this->_partitionIndex; }

        /// <summary>
        /// 获取包围盒
        /// </summary>
        /// <param name="gridBBox">包围盒</param>
        void getBBox(DGGridBBox& gridBBox) { gridBBox = this->_gridBox; }

        /// <summary>
        /// 设置包围盒
        /// </summary>
        /// <param name="gridBBox">包围盒</param>
        bool setBBox(DGGridBBox gridBBox);

        /// <summary>
        /// 获取数据大小
        /// </summary>
        /// <param name="xSize">列数</param>
        /// <param name="ySize">行数</param>
        void getSize(long long int& xSize, long long int& ySize) { xSize = _gridBox.rowSize; ySize = _gridBox.colSize; }

        /// <summary>
        /// 根据code获取单个属性值
        /// </summary>
        /// <param name="code">code</param>
        T getData(DGGSElementCode code);

        /// <summary>
        /// 根据经纬度获取单个属性值
        /// </summary>
        /// <param name="coord">经纬度</param>
        T getData(GeoCoord coord);

        /// <summary>
        /// 根据code设置单个属性值
        /// </summary>
        /// <param name="code">code</param>
        /// <param name="value">格网值</param>
        bool setData(DGGSElementCode code, T value);

        /// <summary>
        /// 根据经纬度设置单个属性值
        /// </summary>
        /// <param name="coord">经纬度</param>
        /// <param name="value">格网值</param>
        bool setData(GeoCoord coord, T value);

        DGGSFieldPartition<T>& operator=(const DGGSFieldPartition<T>& other);

        void deepCopy( DGGSFieldPartition<T> other);

    };

    template<typename T>
    bool DGGSFieldPartition<T>::setNoDataValue(T noDataValue)
    {
        if( this->_noDataValue == noDataValue)
            return true;
        this->_noDataValue = noDataValue;
        return true;
    }

    template<typename T>
    void DGGSFieldPartition<T>::deepCopy( DGGSFieldPartition<T> other)
    {
        this->_gridType = other._gridType;
        this->_pKernel = CreateKernelObj(this->_gridType);
        this->_level = other._level;
        this->_gridBox = other._gridBox;
        this->setBBox(other._gridBox);
        this->_noDataValue = other._noDataValue;
        this->_partitionIndex = other._partitionIndex;

        if (other._data != nullptr)
        {
            long long int xSize = 0, ySize = 0;
            this->getSize(xSize, ySize);
            for (int i = 0; i < xSize * ySize; i++)
                this->_data[i] = other._data[i];
        }
    }

    template<typename T>
    void DGGSFieldPartition<T>::setPartitionIndex(int partitionIndex)
    {
        this->_partitionIndex = partitionIndex;
    }

    template<typename T>
    DGGSFieldPartition<T>& DGGSFieldPartition<T>::operator=(const DGGSFieldPartition<T>& other)
    {
        if (this != &other) {
            this->_gridType = other._gridType;
            this->_pKernel = CreateKernelObj(this->_gridType);
            this->_level = other._level;
            this->_gridBox = other._gridBox;
            this->setBBox(other._gridBox);
            this->_noDataValue = other._noDataValue;
            this->_partitionIndex = other._partitionIndex;

            if (other._data != nullptr)
            {
                long long int xSize = 0, ySize = 0;
                this->getSize(xSize, ySize);
                for (int i = 0; i < xSize * ySize; i++)
                    this->_data[i] = other._data[i];
            }
        }
        return *this;
    }

    template<typename T>
    bool DGGSFieldPartition<T>::setData(GeoCoord coord, T value)
    {
        if (this->_data == nullptr)
            return false;
        DGGSElementCode code;
        code.elementType = DGGSElementType::Cell;
        code.level = this->_level;
        code.levelZ = -1;
        this->_pKernel->encode(coord, code);
        return this->setData(code, value);
    }

    template<typename T>
    bool DGGSFieldPartition<T>::setData(DGGSElementCode code, T value)
    {
        if (this->_data == nullptr)
            return false;
        if (code.basePartition == this->_partitionIndex)
        {
            this->_data[(code.row - this->_gridBox.rowMin) * this->_gridBox.colSize + code.col - (this->_gridBox.colMax + 1 - this->_gridBox.colSize)] = value;
            return true;
        }
        else
            return false;

    }

    template<typename T>
    DGGSFieldPartition<T>::DGGSFieldPartition(int level, DGGSGridType gridType)
    {
        this->_gridType = gridType;
        this->_pKernel = CreateKernelObj(this->_gridType);
        this->_level = level;
    }

    template<typename T>
    T DGGSFieldPartition<T>::getData(GeoCoord coord)
    {
        if (this->_data == nullptr)
            return (T)noData;
        DGGSElementCode code;
        code.elementType = DGGSElementType::Cell;
        code.level = this->_level;
        this->_pKernel->encode(coord, code);

        return this->getData(code);
    }

    template<typename T>
    T DGGSFieldPartition<T>::getData(DGGSElementCode code)
    {
        if (this->_data == nullptr)
            return (T)this->_noDataValue;
        if (code.basePartition == this->_partitionIndex&&
                (code.row - this->_gridBox.rowMin)>=0&&(code.row - this->_gridBox.rowMin)<this->_gridBox.rowSize&&
                code.col - (this->_gridBox.colMax + 1 - this->_gridBox.colSize)>=0&&code.col<=this->_gridBox.colMax
        )
            return this->_data[(code.row - this->_gridBox.rowMin) * this->_gridBox.colSize + code.col - (this->_gridBox.colMax + 1 - this->_gridBox.colSize)];
        else
            return (T)this->_noDataValue;
    }

    template<typename T>
    bool DGGSFieldPartition<T>::setBBox(DGGridBBox gridBBox)
    {
        this->_gridBox = gridBBox;
        long long int xSize = this->_gridBox.rowSize;
        long long int ySize = this->_gridBox.colSize;
        if (xSize <= 0 || ySize <= 0)
            return false;
        if (this->_data != nullptr)
            delete[]this->_data;
        this->_data = nullptr;
        this->_data = new T[xSize * ySize];

        for(int i=0;i<xSize*ySize;i++)
            this->_data[i]=noData;

        return true;
    }

    template<typename T>
    DGGSFieldPartition<T>::~DGGSFieldPartition()
    {
        if (this->_data != nullptr)
            delete[]this->_data;
        if (this->_pKernel != nullptr)
            DestroyKernelObj(this->_pKernel);
    }


}
_NNU_DGGS_END